package org.ricks.agent;

import org.ricks.AppLife;
import org.ricks.log.Logger;
import org.ricks.utils.PathUtil;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author chenwei
 * @Title:
 * @Package
 * @Description:
 * @date 2020/11/1319:15
 */
public class FileWatcher implements AppLife {

    /**
     * 监视的文件目录
     */
    private Path watchDirectory;
    /**
     * 监视的文件类型正则表达式
     */
    private FilenameFilter filenameFilter;
    /**
     * 监视到的文件监听器
     */
    private FileListener fileListener;

    private boolean isShutdown= false;

    private WatchService watchService;

    {
        try {
            watchService = FileSystems.getDefault().newWatchService();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private ExecutorService executorService = Executors.newSingleThreadExecutor();


    public FileWatcher(String watchDirectory, FilenameFilter filenameFilter, FileListener fileListener) {
        this.watchDirectory = Paths.get(PathUtil.currPath() + "//" + watchDirectory);
        this.filenameFilter = filenameFilter;
        this.fileListener = fileListener;
    }

    public FileWatcher(String watchDirectory, FileListener fileListener) {
        this(watchDirectory, null, fileListener);
    }


    /**
     * 监听目录，根据文件的变化处理文件
     */
    public void watch() {
        // 监听新文件
        try {
            File file = this.watchDirectory.toFile();
            if(!file.exists()) file.mkdirs();
            Files.walkFileTree(this.watchDirectory, new SimpleFileVisitor<Path>() {

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)  {
                    register(dir);
                    return FileVisitResult.CONTINUE;
                }
            });
            executorService.execute(() -> watchTask(watchService));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void register(Path path) {
        try {
            if(path.toFile().isDirectory()) {
                Logger.info("start  register watcher file:{}", path.getFileName());
                path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void watchTask(WatchService watchService) {
        while (!isShutdown) {
            try {
                WatchKey watchKey = watchService.take();
                for (WatchEvent event : watchKey.pollEvents()) {
                    WatchEvent.Kind eventKind = event.kind();
                    if (eventKind == StandardWatchEventKinds.OVERFLOW) {
                        continue;
                    }
                    String fileName = event.context().toString();
                    Path file = (Path)watchKey.watchable();
                    file = file.resolve(fileName);
                    if(file.toFile().length() <= 0) continue;
                    //文件名不匹配
                    if (this.filenameFilter != null && !this.filenameFilter.accept(this.watchDirectory.toFile(), fileName)) {
                     continue;
                    }
//                    Path file = Paths.get(this.watchDirectory.toString(), fileName); 此种方法无法load 子文件的子文件
                    if (eventKind == StandardWatchEventKinds.ENTRY_CREATE) {
                        if (file.toFile().getCanonicalFile().isDirectory()) {
                            register(file);
                            File[] files = file.toFile().listFiles();
                            Arrays.stream(files).forEach(f -> register(f.toPath()));
                        } else this.fileListener.onCreate(file);
                    } else if (eventKind == StandardWatchEventKinds.ENTRY_MODIFY) {
                        this.fileListener.onModify(file);
                    } else if (eventKind == StandardWatchEventKinds.ENTRY_DELETE) {
                        this.fileListener.onDelete(file);
                    }
                }
                boolean isKeyValid = watchKey.reset();
                if (!isKeyValid) {
                    break;
                }
            } catch (InterruptedException | IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void shutdown() {
        try {
            System.err.println("开始销毁watcher线程........");
            watchService.close();
            executorService.shutdown();
            isShutdown = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
